/***********************************************************************

	This file is part of KEEL-software, the Data Mining tool for regression, 
	classification, clustering, pattern mining and so on.

	Copyright (C) 2004-2010
	
	F. Herrera (herrera@decsai.ugr.es)
    L. Sánchez (luciano@uniovi.es)
    J. Alcalá-Fdez (jalcala@decsai.ugr.es)
    S. García (sglopez@ujaen.es)
    A. Fernández (alberto.fernandez@ujaen.es)
    J. Luengo (julianlm@decsai.ugr.es)

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see http://www.gnu.org/licenses/
  
**********************************************************************/



package keel.Algorithms.Subgroup_Discovery.SDIGA.SDIGA;

import org.core.*;

/**
 * <p>Methods to define the genetic algorithm and to apply operators and reproduction schema
     * 
 * @author Writed by Pedro González (University of Jaen) 22/08/2004
 * @author Modified by Pedro González (University of Jaen) 4/08/2007
 * @author Modified by Cristóbal J. Carmona (University of Jaen) 20/04/2010
 * @version 2.0
 * @since JDK1.5
 * </p>
 */
public class Genetic {
    /**
     * <p>
     * Methods to define the genetic algorithm and to apply operators and reproduction schema
     * </p>
     */

    private int descendientes;          // Number of descendants generated in the iteration of the GA
    private int long_poblacion;         // Number of individuals of the population
    private int n_eval;                 // Number of evaluations per ejecution
    private float cross_prob;           // Cross probability
    private float mut_prob;             // Mutation probability
    private int Mu_next;                // Next gen to mutate
    private float min_conf;             // Minimun value for the confidence
    private String RulesRep = "CAN";    // Rules representation
    private float w1;                   // Weight for objective 1
    private float w2;                   // Weight for objective 2
    private float w3;                   // Weight for objective 3
    private String Obj1;                // Quality measures used as objective 1
    private String Obj2;                // Quality measures used as objective 1
    private String Obj3;                // Quality measures used as objective 1

    private boolean l_search;           // Perform Local Search or not
    private int Gen;                    // Number of generations performed by the GA
    private int Trials;                 // Number of evaluated chromosomes

    private Population Inter;           // Local Inter population generated by applying genetic ops
    private Population Des;             // Local Population generated by cross and reproduction ops
    private Population poblac;          // Main Population
  

    /**
     * <p>
     * Methods to get the lenght of the population
     * </p>
     * @return              Length of the population
     */
    public int getLenghtPop () {
        return long_poblacion;
    }

    /**
     * <p>
     * Methods to set the lenght of the population
     * </p>
     * @param value              Length of the population
     */
    public void setLenghtPop (int value) {
        long_poblacion = value;    }

    
    /**
     * <p>
     * Sets the value for indicating the position of the next mutation
     * </p>
     * @param value             Value of the variable Mu_next
     */
    public void setMuNext (int value) {
        Mu_next = value;
    }
        
    
    /**
     * <p>
     * Methods to get the number of evaluation to perform in an iteration of the GA
     * </p>
     * @return                  Number of evaluations
     */
    public int getNEval () {
        return n_eval;    }


    /**
     * <p>
     * Methods to set the number of evaluation to perform in an iteration of the GA
     * </p>
     * @param value             Number of evaluations
     */
    public void setNEval (int value) {
        n_eval = value;    }

    
    /**
     * <p>
     * Methods to get the value for the minimum confidence of the rules to be generated
     * </p>
     * @return                  Minimum confidence
     */
    public float getMinConf () {
        return min_conf;    }


    /**
     * <p>
     * Methods to set the value for the minimum confidence of the rules to be generated
     * </p>
     * @param value             Minimum confidence
     */
    public void setMinConf (float value) {
        min_conf = value;    }

    /**
     * <p>
     * Gets the representation of the rules
     * </p>
     * @return              Representation of the rules
     */
    public String getRulesRep(){
        return RulesRep;
    }

    /**
     * <p>
     * Sets the representation of the rules
     * </p>
     * @param value              Representation of the rules
     */
    public void setRulesRep(String value){
        RulesRep = value;
    }

    /**
     * <p>
     * Methods to get the value for the crossover probability
     * </p>
     * @return                  Crossover probability
     */
    public float getProbCross () {
        return cross_prob;
    }

    /**
     * <p>
     * Methods to set the value for the crossover probability
     * </p>
     * @param value             Crossover probability
     */
    public void setProbCross (float value) {
        cross_prob = value;
    }

    /**
     * <p>
     * Methods to get the value for the mutation probability
     * </p>
     * @return                  Mutation probability
     */
    public float getProbMut () {
        return mut_prob;
    }

    /**
     * <p>
     * Methods to set the value for the mutation probability
     * </p>
     * @param value             Mutation probability
     */
    public void setProbMut (float value) {
        mut_prob= value;
    }



    /**
     * <p>
     * Methods to get the value for the weight of the objective 1
     * </p>
     * @return              Weight of the objective 1
     */
    public float getW1 () {
        return w1;
    }

    /**
     * <p>
     * Methods to get the value for the weight of the objective 2
     * </p>
     * @return              Weight of the objective 2
     */
    public float getW2 () {
        return w2;
    }

    /**
     * <p>
     * Methods to get the value for the weight of the objective 3
     * </p>
     * @return              Weight of the objective 3
     */
    public float getW3 () {
        return w3;
    }

    /**
     * <p>
     * Methods to get the value for the weight of the objective in a position
     * </p>
     * @param               pos of the objective
     * @return              Weight of the objective
     */
    public float getW(int pos){
        if (pos==1) {
            return w1;
        } else if (pos==2) {
            return w2;
        } else return w3;
    }


    /**
     * <p>
     * Methods to set the value for the weight of the objective 1
     * </p>
     * @param value             Weight of the objective 1
     */
    public void setW1 (float value) {
        w1 = value;
    }

    /**
     * <p>
     * Methods to set the value for the weight of the objective 2
     * </p>
     * @param value             Weight of the objective 2
     */
    public void setW2 (float value) {
        w2 = value;
    }

    /**
     * <p>
     * Methods to set the value for the weight of the objective 3
     * </p>
     * @param value             Weight of the objective 3
     */
    public void setW3 (float value) {
        w3 = value;
    }


    /**
     * <p>
     * Methods to get the name of the quality measure as objective 1
     * </p>
     * @return              Quality measure of the objective 1
     */
    public String getObj1 () {
        return Obj1;    
    }

    /**
     * <p>
     * Methods to get the name of the quality measure as objective 2
     * </p>
     * @return              Quality measure of the objective 2
     */
    public String getObj2 () {
        return Obj2;    
    }

    /**
     * <p>
     * Methods to get the name of the quality measure as objective 3
     * </p>
     * @return              Quality measure of the objective 3
     */
    public String getObj3 () {
        return Obj3;    
    }

    /**
     * <p>
     * Methods to get the name of the quality measure as objective in a position
     * </p>
     * @param pos           Position of the quality measure
     * @return              Quality measure of the objective
     */
    public String getObj(int pos){
        if (pos==1) {
            return Obj1;
        } else if (pos==2) {
            return Obj2;
        } else return Obj3;
    }

    /**
     * <p>
     * Methods to set the value for the quality measures as objective 1
     * </p>
     * @param value             Quality measure as objetive 1
     */
    public void setObj1 (String value) {
        Obj1 = value;
    }

    /**
     * <p>
     * Methods to set the value for the quality measures as objective 2
     * </p>
     * @param value             Quality measure as objetive 2
     */
    public void setObj2 (String value) {
        Obj2 = value;
    }

    /**
     * <p>
     * Methods to set the value for the quality measures as objective 3
     * </p>
     * @param value             Quality measure as objetive 3
     */
    public void setObj3 (String value) {
        Obj3 = value;
    }


    /**
     * <p>
     * Methods to get if local search must be performed
     * </p>
     * @return                      Value of local search
     */
    public boolean getLSearch () {
        return l_search;
    }

    /**
     * <p>
     * Methods to set if local search must be performed
     * </p>
     * @param value                Value of local search
     */
    public void setLSearch (boolean value) {
        l_search = value;    }


    /**
     * <p>
     * Return the position of the best individual of the main population
     * </p>
     * @return              Position of the best individual
     */
    public int getBestGuy () {
        return poblac.getBestGuy();
    }


    /**
     * <p>
     * Get the measures of a single rule of the main population
     * </p>
     * @param pos           Position of the individual
     * @param nFile         Name of the file to write the values
     * @return  the measures of a single rule of the main population
     */
    public QualityMeasures getQualityMeasures (int pos, String nFile) {
        return poblac.getMedidas(pos, nFile);
    }

    /**
     * <p>
     * Evaluates an individual of the main population
     * </p>
     * @param pos           Position of the invidivual
     * @param AG            Genetic algorithm object
     * @param Variables     Structure of the Variables
     * @param Examples      Structure of the Examples
     * @param marcar        Indicates to mark the covered examples
     */
    public void evalIndiv (int pos, Genetic AG, TableVar Variables, TableDat Examples, boolean marcar) {
        poblac.evalIndiv(pos, AG, Variables, Examples, marcar);
    }

    
    /**
     * <p>
     * Returns de hole chromosome of the selected individual
     * </p>
     * @param pos           Position of the individual
     * @return              Canonical chromosome of the individual
     */
    public CromCAN getIndivCromCAN (int pos) {
        return poblac.getIndivCromCAN(pos);
    }

    /**
     * <p>
     * Returns de hole chromosome of the selected individual
     * </p>
     * @param pos           Position of the individual
     * @return              DNF chromosome of the individual
     */
    public CromDNF getIndivCromDNF (int pos) {
        return poblac.getIndivCromDNF(pos);
    }

    
    /**
     * <p>
     * Applies the selection schema of the genetic algorithm
     * </p>
     * @param Variables         Structure of Variables
     */
    public void SelectCAN(TableVar Variables) {
        float perf;
        int i, j, k, best, posicion[];

        posicion = new int[long_poblacion];
        for (i=0;i<long_poblacion;i++) {
            posicion[i] = 0;
        }
        
        // Compute the ranking of each individual of the population: element 0 is the better
        for (i=0;i<long_poblacion;i++) {
            // Find better individual of the rest
            best=-1; 
            perf=0;
            for (j=0;j<long_poblacion;j++) {
                if (posicion[j]==0 && (best==-1 || Utils.BETTER(poblac.getIndivFitness(j), perf))) {
                    // If the individual is better
                    perf = poblac.getIndivFitness(j);
                    best = j;
                }
            }
            // Mark the individual with the ranking
            posicion[best] = i;  
        }
        
        // The intermediary population is the ordered copy
        for (i=0;i<long_poblacion;i++) {
            k = posicion[i];
            for (j=0;j<Variables.getNVars();j++)
                Inter.setCromElem(k,j, poblac.getCromElem(i,j));
            Inter.setIndivFitness(k, poblac.getIndivFitness(i));
            Inter.setIndivEvaluated(k,true);
        }
      
    }
    
    /**
     * <p>
     * Applies the selection schema of the genetic algorithm
     * </p>
     * @param Variables         Structure of Variables
     */
    public void SelectDNF(TableVar Variables) {
        float perf;
        int i, j, k, m, best, posicion[];

        posicion = new int[long_poblacion];
        for (i=0;i<long_poblacion;i++) {
            posicion[i] = 0;
        }

        // Compute the ranking of each individual of the population: element 0 is the better
        for (i=0;i<long_poblacion;i++) {
            // Find better individual of the rest
            best=-1;
            perf=0;
            for (j=0;j<long_poblacion;j++) {
                if (posicion[j]==0 && (best==-1 || Utils.BETTER(poblac.getIndivFitness(j), perf))) {
                    // If the individual is better
                    perf = poblac.getIndivFitness(j);
                    best = j;
                }
            }
            // Mark the individual with the ranking
            posicion[best] = i;
        }

        // The intermediary population is the ordered copy
        for (i=0;i<long_poblacion;i++) {
            k = posicion[i];
            for (j=0;j<Variables.getNVars();j++)
                for (m=0; m<=Variables.getNLabelVar(j); m++)
                    Inter.setCromElemGene(k,j, m, poblac.getCromElemGene(i,j,m));
            Inter.setIndivFitness(k, poblac.getIndivFitness(i));
            Inter.setIndivEvaluated(k,true);
        }

    }

    /**
     * <p>
     * Cross operator for the genetic algorithm, where
     * only cross the two better individuals
     * </p>
     * @param Variables         Structure of Variables
     */
    public void MultipointCrossoverCAN (TableVar Variables) {
        int i, mom, dad, xpoint1, xpoint2;
        
        // We cross the two better individuals (the first ones)
        mom = 0;   // Better individual
        dad = 1;   // Second individual
        
        // Copy the individuals to cross
        for (i=0; i<Variables.getNVars(); i++) {
            Des.setCromElem (0, i, Inter.getCromElem(mom,i));
            Des.setCromElem (1, i, Inter.getCromElem(dad,i));
        }

        // Generation of the two points for the crossover
        xpoint1 = Randomize.Randint (0,(Variables.getNVars()-1));
        if (xpoint1!=Variables.getNVars()-1)
            xpoint2 = Randomize.Randint ((xpoint1+1),(Variables.getNVars()-1));
        else
            xpoint2 = Variables.getNVars()-1;

        // Cross the parts between both points
        for (i=xpoint1;i<=xpoint2;i++) {
            Des.setCromElem(mom, i, Inter.getCromElem(dad,i));
            Des.setCromElem(dad, i, Inter.getCromElem(mom,i));
        }

        // New individuals are not evaluated
        Des.setIndivEvaluated (mom, false);
        Des.setIndivEvaluated (dad, false);
        
    }

    /**
     * <p>
     * Cross operator for the genetic algorithm, where
     * only cross the two better individuals
     * </p>
     * @param Variables         Structure of Variables
     */
    public void MultipointCrossoverDNF (TableVar Variables) {
        int i, j, mom, dad, xpoint1, xpoint2;

        // We cross the two better individuals (the first ones)
        mom = 0;   // Better individual
        dad = 1;   // Second individual

        // Copy the individuals to cross
        for (i=0; i<Variables.getNVars(); i++) {
            for (j=0; j<=Variables.getNLabelVar(i); j++) {
                Des.setCromElemGene(0, i, j, Inter.getCromElemGene(mom,i, j));
                Des.setCromElemGene (1, i, j, Inter.getCromElemGene(dad,i, j));
            }
        }

        // Generation of the two points for the crossover
        xpoint1 = Randomize.Randint (0,(Variables.getNVars()-1));
        if (xpoint1!=Variables.getNVars()-1)
            xpoint2 = Randomize.Randint ((xpoint1+1),(Variables.getNVars()-1));
        else
            xpoint2 = Variables.getNVars()-1;

        // Cross the parts between both points
        for (i=xpoint1;i<=xpoint2;i++) {
            for (j=0; j<=Variables.getNLabelVar(i); j++) {
                Des.setCromElemGene(mom, i, j, Inter.getCromElemGene(dad,i,j));
                Des.setCromElemGene(dad, i, j, Inter.getCromElemGene(mom,i,j));
            }
        }

        // New individuals are not evaluated
        Des.setIndivEvaluated (mom, false);
        Des.setIndivEvaluated (dad, false);

    }

     /**
     * <p>
     * Applies the mutation operator. Uniform biased operator
     * </p>
     * @param Variables             Structure of Variables
     */
    public void MutationCAN (TableVar Variables) {

        int posiciones, i, j, h, eliminar;
        float m;

        posiciones = Variables.getNVars()*long_poblacion;

        if (mut_prob > 0)
            while (Mu_next < posiciones) {
                // Determine the chromosome and gene to be muted
                i = Mu_next/Variables.getNVars();
                j = Mu_next%Variables.getNVars();

                // Copy the chromosome
                for (h=0; h<Variables.getNVars(); h++) {
                        Des.setCromElem(descendientes, h, Inter.getCromElem(i,h));
                }

                // Make the mutation
                // Half the mutations eliminates the variable from the rule
                // The others sets a random value (including the elimination)
                eliminar = Randomize.Randint (0,10);
                if (eliminar <=5){
                    if (!Variables.getContinuous(j))
                        Des.setCromElem(descendientes, j, (int)Variables.getMax(j)+1);
                    else
                        Des.setCromElem(descendientes, j, Variables.getNLabelVar(j));
                }
                else {
                    if (!Variables.getContinuous(j))
                        Des.setCromElem(descendientes, j, Randomize.Randint((int)Variables.getMin(j), (int)Variables.getMax(j)));
                    else
                        Des.setCromElem(descendientes, j, Randomize.Randint(0,(Variables.getNLabelVar(j)-1)));
                }
                descendientes++;

                // Marks the chromosome as no evaluated
                Des.setIndivEvaluated(i,false);

                // Compute next position to be muted
                if (mut_prob<1) {
                    m = (float) Randomize.Rand();
                    Mu_next += Math.ceil (Math.log(m) / Math.log(1.0 - mut_prob));
                }
                else
                    Mu_next += 1;

            }
            Mu_next -= posiciones;

    }

    
    /**
     * <p>
     * Applies the mutation operator. Uniform biased operator
     * </p>
     * @param Variables             Structure of Variables
     */
    public void MutationDNF (TableVar Variables) {

        int posiciones, interv, i, j, l, h, eliminar;
        float m;
        
        posiciones = Variables.getNVars()*long_poblacion;
        
        if (mut_prob > 0)
            while (Mu_next < posiciones) {
                // Determine the chromosome and gene to be muted
                i = Mu_next/Variables.getNVars();
                j = Mu_next%Variables.getNVars();
                
                // Copy the chromosome
                for (h=0; h<Variables.getNVars(); h++) {
                    for (l=0; l<=Variables.getNLabelVar(h); l++)
                        Des.setCromElemGene(descendientes, h, l, Inter.getCromElemGene(i,h,l));
                }
                
                // Make the mutation
                // Half the mutations eliminates the variable from the rule
                // The others sets a random value (including the elimination)
                eliminar = Randomize.Randint (0,10);
                if (eliminar <=5){
                    for (l=0; l<=Variables.getNLabelVar(j); l++)
                       Des.setCromElemGene(descendientes, j, l, 0);
                }
                else {
                    interv = 0;
                    for (l=0; l<Variables.getNLabelVar(j); l++) {
                       Des.setCromElemGene(descendientes, j, l, Randomize.Randint(0,1));
                       if (Des.getCromElemGene(descendientes, j, l)==1)
                          interv ++;
                    }
                    // si no interviene ningún valor o intervienen todos, la variable no interviene
                    if (interv==0 || interv==Variables.getNLabelVar(j))
                        Des.setCromElemGene(descendientes, j, Variables.getNLabelVar(j), 0);
                    else
                        Des.setCromElemGene(descendientes, j, Variables.getNLabelVar(j), 1);
                }
                descendientes++;

                // Marks the chromosome as no evaluated
                Des.setIndivEvaluated(i,false);

                // Compute next position to be muted
                if (mut_prob<1) {
                    m = (float) Randomize.Rand();
                    Mu_next += Math.ceil (Math.log(m) / Math.log(1.0 - mut_prob));
                }
                else
                    Mu_next += 1;

            }
            Mu_next -= posiciones;
            
    }

    /**
     * <p>
     * Uses a Steady Step method
     * </p>
     * @param Variables             Structure of Variables
     */
    public void SteadyStepReproductionDNF (TableVar Variables) {

        int i, j, k, pos;
        int[] indices = new int[long_poblacion];
        float[] fit = new float[long_poblacion];

        for (i=0; i<long_poblacion; i++) {
            fit[i]= poblac.getIndivFitness(i);
            indices[i] = i;
        }

        // Sort the fitness array of actual population and store the values at "indices"
        Utils.OrDecIndex (fit,0,long_poblacion-1,indices);  // To maximise

        for (i=0; i<descendientes; i++) {	// For each descendant
            pos = indices[long_poblacion-1-i];
            for (j=0; j<Variables.getNVars(); j++) {
                for (k=0; k<=Variables.getNLabelVar(j); k++)
                    poblac.setCromElemGene(pos, j, k, Des.getCromElemGene(i,j,k));
            }
            poblac.setIndivEvaluated(pos,false);
        }

    }

    
    /**
     * <p>
     * Uses a Steady Step method
     * </p>
     * @param Variables             Structure of Variables
     */
    public void SteadyStepReproductionCAN (TableVar Variables) {

        int i, j, pos;
        int[] indices = new int[long_poblacion];
        float[] fit = new float[long_poblacion];
        
        for (i=0; i<long_poblacion; i++) {
            fit[i]= poblac.getIndivFitness(i);
            indices[i] = i;    
        }
        
        // Sort the fitness array of actual population and store the values at "indices"
        Utils.OrDecIndex (fit,0,long_poblacion-1,indices);  // To maximise
        
        for (i=0; i<descendientes; i++) {	// For each descendant
            pos = indices[long_poblacion-1-i];		
            for (j=0; j<Variables.getNVars(); j++) {
                poblac.setCromElem(pos, j, Des.getCromElem(i,j));
            }
            poblac.setIndivEvaluated(pos,false);
        }

    }

   
    /**
     * <p>
     * Locally improves the rule generated. This method locally optimizes on support.
     * </p>
     * @param Variables             Structure of Variables
     * @param Examples              Structure of Examples
     * @return                      If the improvement is performed
     */
    public boolean LocalImprovementCAN (TableVar Variables, TableDat Examples) {

        Individual solucion = new IndCAN(Variables.getNVars());
        QualityMeasures res, new_res;
        int best, initialvalue, cambio;
        int[] indices = new int[Variables.getNVars()];
        float[] evalua = new float[Variables.getNVars()];
        boolean improvement = true;
        boolean retValue = false;
        int control;
        
        // Store the pos of the best guy
        best = poblac.getBestGuy();
        
        // Copy the rule to improve
        for (int i=0; i<Variables.getNVars(); i++)
            solucion.setCromElem(i, this.poblac.getCromElem(best,i));

        // Evaluate de rule and get the measures
        solucion.evalInd(this, Variables, Examples, false);
        Trials++;
        res = solucion.getMedidas();
        control = 0;

        while (improvement) {
            improvement = false; // By now, there is no improvement
            for (int i=0; i<Variables.getNVars(); i++) {
                indices[i]=i;
                if (Variables.getType(i)=='e') {
                    // Discrete Variable
                    if (solucion.getCromElem(i)==(Variables.getMax(i)+1)){
                        // If variable does not actually takes part in the rule
                        evalua[i] = -1;
                    } else {
                        initialvalue = solucion.getCromElem(i);
                        solucion.setCromElem(i, (int)Variables.getMax(i)+1);	// Mark Doesn't take part
                        solucion.evalInd(this, Variables, Examples, false);
                        Trials++;  
                        new_res = solucion.getMedidas();

                       if ((new_res.getCnf() >= res.getCnf())&&(new_res.getSup() >= res.getSup())) {
                            evalua[i] = new_res.getSup();
                            improvement = true;
                        } else {
                            evalua[i]=-1;
                        }
                        // Restore initial values
                        solucion.setCromElem(i, initialvalue);
                    }
                } else {
                    // Continuous variable
                    if (solucion.getCromElem(i)==(Variables.getNLabelVar(i))){
                        // If variable does not take part
                        evalua[i] = -1;
                    } else {
                        initialvalue = solucion.getCromElem(i);
                        solucion.setCromElem(i, (int)Variables.getNLabelVar(i));	// Mark does not take part
                        solucion.evalInd(this, Variables, Examples, false);
                        Trials++;  
                        new_res = solucion.getMedidas();

                        if ((new_res.getCnf() >= res.getCnf())&&(new_res.getSup() >= res.getSup())) {
                            evalua[i] = new_res.getSup();
                            improvement = true;
                        } else {
                            evalua[i]=-1;
                        }
                        // Restore initial values
                        solucion.setCromElem(i, initialvalue);
                    }
                }
            }
            
            if (improvement) {
                // Sorts in ascendent order to minimise
                // "evalua" contents the support of each modification; seeking for the better
                Utils.OrCrecIndex (evalua,0,Variables.getNVars()-1,indices);
                cambio = indices[Variables.getNVars()-1];
                if (Variables.getType(cambio)=='e')
                    solucion.setCromElem(cambio, (int)Variables.getMax(cambio)+1);
                else
                    solucion.setCromElem(cambio, (int)Variables.getNLabelVar(cambio));
                res.setSup(evalua[Variables.getNVars()-1]);
                
                // If there is improvement, set return value to true
                retValue = true;
                
            }
        }  // End while

        for(int i=0; i<Variables.getNVars(); i++){
            if(solucion.getCromElem(i)==Variables.getNLabelVar(i))
                control++;
        }

        // Store the new rule if it was improved
        if ((retValue==true)&&(control!=Variables.getNVars())){

            for (int i=0; i<Variables.getNVars(); i++)
                poblac.setCromElem(best, i, solucion.getCromElem(i));

            // Mark the rule as not evaluated
            poblac.setIndivEvaluated(best,false);
        }
        
        return retValue;

    }

    /**
     * <p>
     * Locally improves the rule generated. This method locally optimizes on support.
     * </p>
     * @param Variables             Structure of Variables
     * @param Examples              Structure of Examples
     * @return                      If the improvement is performed
     */
    public boolean LocalImprovementDNF (TableVar Variables, TableDat Examples) {
        IndDNF solucion = new IndDNF(Variables.getNVars(), Variables);
        QualityMeasures res, new_res;
        int mejor, cambio;
        int[] indices = new int[Variables.getNVars()];
        float[] evalua = new float[Variables.getNVars()];
        boolean improvement = true;
        boolean retValue = false;
        int control;

        // Store the pos of the best guy in "mejor"
        mejor = poblac.getBestGuy();

        // Copy in "solucion" the rule to improve
        for (int i=0; i<Variables.getNVars(); i++)
            for (int j=0; j<=Variables.getNLabelVar(i); j++)
                solucion.setCromElemGene(i, j, poblac.getCromElemGene(mejor,i,j));

        // Evaluate de rule and get the measures
        solucion.evalInd(this, Variables, Examples, false);
        Trials++;  // Increment the number of trials
        res = solucion.getMedidas();

        control = 1;

        while ((improvement)&&(control<Variables.getNVars())) {
            improvement = false; // By now, there is no improvement
            for (int i=0; i<Variables.getNVars(); i++) {
                indices[i]=i;
                // It does not matter if the variable is discrete or continuous
                if (solucion.getCromElemGene(i,Variables.getNLabelVar(i))==0)
                    // If variable does not actually takes part in the rule
                    evalua[i] = -1;
                else {
                    // The initial value is not stored cause we know the value is 1
                    solucion.setCromElemGene(i, Variables.getNLabelVar(i),0);
                    solucion.evalInd(this, Variables, Examples, false);
                    Trials++;
                    new_res = solucion.getMedidas();

                    if ((new_res.getCnf() >= res.getCnf())&&(new_res.getSup() >= res.getSup())) {
                        evalua[i] = new_res.getSup();
                        improvement = true;
                    }
                    else
                        evalua[i]=-1;
                    // Restore initial values of "solucion"
                    solucion.setCromElemGene(i, Variables.getNLabelVar(i), 1);
                }
            }

            if (improvement) {
                Utils.OrCrecIndex (evalua,0,Variables.getNVars()-1,indices);
                cambio = indices[Variables.getNVars()-1];
                for (int j=0; j<=Variables.getNLabelVar(cambio); j++)
                    solucion.setCromElemGene(cambio, j, 0);

                res.setSup(evalua[Variables.getNVars()-1]);
                // Important: take into account not to eliminate all the variables of the rule

                // If there is improvement, set return value to true
                retValue = true;
                control++;

            }
        }  // End while "improvement"

        // Store the new rule if it was improved
        if (retValue==true){
            for (int i=0; i<Variables.getNVars(); i++)
                for (int j=0; j<=Variables.getNLabelVar(i); j++)
                    poblac.setCromElemGene(mejor, i, j, solucion.getCromElemGene(i,j));
            poblac.setIndivEvaluated(mejor,false);
        }

        return retValue;

    }


    /**
     * <p>
     * Composes the genetic algorithm applying the operators
     * </p>
     * @param Variables             Structure of Variables
     * @param Examples              Structure of Examples
     * @param nFile                 File to write the process of the genetic algorithm
     */
    public void GeneticAlgorithm (TableVar Variables, TableDat Examples, String nFile) {

        String contents;

        // Creates and initialices the population
        poblac = new Population(long_poblacion, Variables.getNVars(), RulesRep, Variables);
        poblac.RndInitPop (Variables);

        // Creates the populations "Inter" and "Des" to aply the genetic operations
        Inter = new Population(long_poblacion, Variables.getNVars(), RulesRep, Variables);
        Des   = new Population(2*long_poblacion, Variables.getNVars(), RulesRep, Variables);
        
        // Inicialisation of variables
        Trials = 0;
        Gen = 0;

        // Evaluation of the individuals of the initial population
        Trials+=poblac.evalPop (this, Variables, Examples, false);
                
        // Increments the generation number
        Gen++;   
        
        // General cycle of the genetic algorithm
        do {
            // Selection by Baker method
            if(RulesRep.compareTo("CAN")==0)
                SelectCAN (Variables);
            else SelectDNF (Variables);

            // Crossover
            descendientes = 0;
            if(RulesRep.compareTo("CAN")==0)
                MultipointCrossoverCAN (Variables);
            else MultipointCrossoverDNF (Variables);
            descendientes = 2;
            
            // Mutation
            if(RulesRep.compareTo("CAN")==0)
                MutationCAN (Variables);
            else MutationDNF (Variables);

            // Reproduction
            if(RulesRep.compareTo("CAN")==0)
                SteadyStepReproductionCAN(Variables);
            else SteadyStepReproductionDNF(Variables);

            // Evaluation of the new population
            Trials+=poblac.evalPop (this, Variables, Examples, false);

            // Next generation
            Gen++;

        // GA finishes when the number of evaluations is reached
        } while (Trials <= n_eval);    

        contents = "   # " + (Gen-1) + " generations, " + Trials + " evaluations\n";
        Files.addToFile(nFile, contents);
        
    }
    
    
    /**
     * <p>
     * Creates a new instance of Genetic
     * </p>
     */
    public Genetic() {
    }

    
}
